﻿#region MIT
/*
Engine Of Evermore (http://code.google.com/p/engineofevermore/)
Copyright (c) 2008, 2009 Michael Woerister

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;
using EngineOfEvermore.Common;
using System.Xml.Linq;
using System.IO;
using System.Xml;


namespace EngineOfEvermore.Scene
{
    /// <summary>
    /// A component represents some aspect of the containing scene-object, for example its visual
    /// properties.
    /// </summary>
    /// <remarks>
    /// Every SceneObjectComponent implementation must have a default constructor. 
    /// A SceneObjectComponent must also be cloneable.
    /// </remarks>
    public abstract class SceneObjectComponent
    {
        #region Variables
        SceneObject _owner;
        SceneGraph _scene;
        #endregion

        #region Properties
        public SceneObject Owner
        {
            get { return _owner; }
        }

        public SceneGraph Scene
        {
            get { return _scene; }
        }
        #endregion

        #region Methods
        internal protected virtual void OnAddToSceneObject( SceneObject sceneObject )
        {
            Debug.Assert( sceneObject != null );
            Debug.Assert( _owner == null );

            _owner = sceneObject;
        }

        internal protected virtual void OnRemoveFromSceneObject( SceneObject sceneObject )
        {
            Debug.Assert( _owner == sceneObject );

            _owner = null;
        }

        internal protected virtual void OnSiblingAdded( SceneObjectComponent sibling ) { }

        internal protected virtual void OnSiblingRemoved( SceneObjectComponent sibling ) { }

        internal protected virtual void OnAddToScene( SceneGraph scene )
        {
            Debug.Assert( scene != null );
            Debug.Assert( _scene == null );

            _scene = scene;
        }

        internal protected virtual void OnRemoveFromScene( SceneGraph scene )
        {
            Debug.Assert( _scene == scene );

            _scene = null;
        }

        public abstract SceneObjectComponent Clone();
        #endregion	
    }

    public class SceneObjectComponentSerializer :
        PolymorphicSerializer<SceneObjectComponent>
    {
        public static readonly SceneObjectComponentSerializer Instance =
            new SceneObjectComponentSerializer();

        public void Deserialize( XElement xml, SceneObject sceneObject )
        {
            var matchesType = _typeChecker[xml.Name];

            for ( int i = 0; i < sceneObject.ComponentCount; ++i )
            {
                var component = sceneObject.GetComponent( i );

                if ( matchesType( component ) )
                {
                    Deserialize( xml, component );
                    return;
                }
            }

            var factory = _factories[xml.Name];

            SceneObjectComponent newComponent = factory();

            Deserialize( xml, newComponent );

            sceneObject.AddComponent( newComponent );
        }
    }

    namespace Test
    {
        using NUnit.Framework;

        [TestFixture]
        public class SceneObjectComponentSerializerTest
        {
            class TestComponent1 : SceneObjectComponent
            {
                public override SceneObjectComponent Clone()
                {
                    throw new NotImplementedException();
                }
            }

            class TestComponent2 : SceneObjectComponent
            {
                public int PropX = 10;

                public override SceneObjectComponent Clone()
                {
                    throw new NotImplementedException();
                }

                public static void Deserialize( XElement xml, SceneObjectComponent component )
                {
                    Debug.Assert( component is TestComponent2 );

                    var c = (TestComponent2) component;

                    c.PropX = IntSerializer.Deserialize( xml.Element( "PropX" ) );
                }
            }

            [SetUp]
            public void SetUp()
            {
                SceneObjectComponentSerializer.Instance.ClearRegistry();
            }

            [Test]
            public void TestRegisterSerializer() 
            {
                Assert.That( SceneObjectComponentSerializer.Instance
                    .HasSerializerFor<TestComponent1>( "TestComponent1" ), 
                    Is.False );

                SceneObjectComponentSerializer.Instance.RegisterSerializer<TestComponent1>( 
                    "TestComponent1", c => null );

                Assert.That( SceneObjectComponentSerializer.Instance
                    .HasSerializerFor<TestComponent1>( "TestComponent1" ), 
                    Is.True );
            }

            
            [Test]
            public void TestSerialize()
            {
                bool rightSerializerWasChosen = false;

                Func<SceneObjectComponent, XElement> rightSerializer =
                    c => { rightSerializerWasChosen = true; return null; };
                Func<SceneObjectComponent, XElement> wrongSerializer = ( c => null );

                SceneObjectComponentSerializer.Instance.RegisterSerializer<TestComponent1>( 
                    "TestComponent1", rightSerializer );

                SceneObjectComponentSerializer.Instance.RegisterSerializer<TestComponent2>( 
                    "TestComponent2", wrongSerializer );

                var component = new TestComponent1();

                SceneObjectComponentSerializer.Instance.Serialize( component );

                Assert.That( rightSerializerWasChosen );
            }

            [Test]
            public void TestRegisterDeserializer()
            {
                Assert.That( SceneObjectComponentSerializer.Instance
                    .HasDeserializerFor<TestComponent1>( "TestComponent1" ), Is.False );

                SceneObjectComponentSerializer.Instance.RegisterDeserializer<TestComponent1>( 
                    "TestComponent1",
                    ( xml, component ) => {} 
                    );

                Assert.That( SceneObjectComponentSerializer.Instance
                    .HasDeserializerFor<TestComponent1>( "TestComponent1" ), Is.True );
            }

            [Test]
            public void TestDeserialize()
            {
                bool rightDeserializerWasSelected = false;

                Action<XElement, SceneObjectComponent> rightDeserializer =
                    ( w, component ) => { rightDeserializerWasSelected = true; };
                Action<XElement, SceneObjectComponent> wrongDeserializer =
                    ( w, component ) => {};

                SceneObjectComponentSerializer.Instance.RegisterDeserializer<TestComponent1>( 
                    "TestComponent1", rightDeserializer );

                SceneObjectComponentSerializer.Instance.RegisterDeserializer<TestComponent2>(
                    "TestComponent2", wrongDeserializer );

                var element = new XElement( "TestComponent1" );

                TestComponent1 testComponent1 = new TestComponent1();

                SceneObjectComponentSerializer.Instance.Deserialize( element, testComponent1 );

                Assert.That( rightDeserializerWasSelected );
            }

            [Test]
            public void TestDeserializeForSceneObject_ComponentExists()
            {
                SceneObjectComponentSerializer.Instance.RegisterDeserializer<TestComponent2>(
                    "TestComponent2", TestComponent2.Deserialize );

                SceneObject sceneObject = new SceneObject();

                sceneObject.AddComponent( new TestComponent2() );

                var xml =
                    new XElement( "TestComponent2",
                        new XElement( "PropX", "501" )
                        );

                SceneObjectComponentSerializer.Instance.Deserialize( xml, sceneObject );

                Assert.That( sceneObject.GetComponent<TestComponent2>().PropX, Is.EqualTo( 501 ) );
            }

            [Test]
            public void TestDeserializeForSceneObject_ComponentDoesNotExist()
            {
                SceneObjectComponentSerializer.Instance.RegisterDeserializer<TestComponent2>(
                    "TestComponent2", TestComponent2.Deserialize );

                SceneObject sceneObject = new SceneObject();

                var xml =
                    new XElement( "TestComponent2",
                        new XElement( "PropX", "507" )
                        );

                SceneObjectComponentSerializer.Instance.Deserialize( xml, sceneObject );

                Assert.That( sceneObject.GetComponent<TestComponent2>().PropX, Is.EqualTo( 507 ) );
            }
        }
    }
}
